{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "github地址:[https://github.com/cheesezh/python_design_patterns](https://github.com/cheesezh/python_design_patterns)\n",
    "\n",
    "## 题目\n",
    "用程序模拟以下情景\n",
    "- 员工向经理发起加薪申请，经理无权决定，需要向总监汇报，加薪额度超过总监权力范围，需要向总经理汇报; \n",
    "- 员工还可以提交请加申请，经理可以决定2天以下的假，总监可以决定5天以下的假，其余都要上报总经理;\n",
    "\n",
    "## 基础版本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "经理:贺贺请求加薪 数量 1000 我无权处理\n",
      "总监:贺贺请求加薪 数量 1000 我无权处理\n",
      "总经理:贺贺请求加薪 数量 1000 再说吧\n",
      "经理:贺贺请求请假 数量 3 我无权处理\n",
      "总监:贺贺请求请假 数量 3 被批准\n",
      "总经理:贺贺请求请假 数量 3 被批准\n"
     ]
    }
   ],
   "source": [
    "class Request():\n",
    "    \n",
    "    def __init__(self):\n",
    "        self.type = None  # 申请类型\n",
    "        self.content = None  # 申请内容\n",
    "        self.number = 0  # 申请数量\n",
    "    \n",
    "    \n",
    "class Manager():\n",
    "    \n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        \n",
    "    def get_result(self, manager_level, request):\n",
    "        if manager_level == \"经理\":\n",
    "            if request.type == \"请假\" and request.number <=2:\n",
    "                print(\"{}:{} 数量 {} 被批准\".format(self.name, request.content, request.number))\n",
    "            else:\n",
    "                print(\"{}:{} 数量 {} 我无权处理\".format(self.name, request.content, request.number))\n",
    "        elif manager_level == \"总监\":\n",
    "            if request.type == \"请假\" and request.number <=5:\n",
    "                print(\"{}:{} 数量 {} 被批准\".format(self.name, request.content, request.number))\n",
    "            else:\n",
    "                print(\"{}:{} 数量 {} 我无权处理\".format(self.name, request.content, request.number))\n",
    "        elif manager_level == \"总经理\":\n",
    "            if request.type == \"请假\":\n",
    "                print(\"{}:{} 数量 {} 被批准\".format(self.name, request.content, request.number))\n",
    "            elif request.type == \"加薪\" and request.number <= 500:\n",
    "                print(\"{}:{} 数量 {} 被批准\".format(self.name, request.content, request.number))\n",
    "            elif request.type == \"加薪\" and request.number > 500:\n",
    "                print(\"{}:{} 数量 {} 再说吧\".format(self.name, request.content, request.number))\n",
    "        \n",
    "def main():\n",
    "    jingli = Manager(\"经理\")\n",
    "    zongjian = Manager(\"总监\")\n",
    "    zongjingli = Manager(\"总经理\")\n",
    "    \n",
    "    request = Request()\n",
    "    request.type = \"加薪\"\n",
    "    request.content = \"贺贺请求加薪\"\n",
    "    request.number = 1000\n",
    "    \n",
    "    jingli.get_result(jingli.name, request)\n",
    "    zongjian.get_result(zongjian.name, request)\n",
    "    zongjingli.get_result(zongjingli.name, request)\n",
    "    \n",
    "    request.type = \"请假\"\n",
    "    request.content = \"贺贺请求请假\"\n",
    "    request.number = 3\n",
    "    \n",
    "    jingli.get_result(jingli.name, request)\n",
    "    zongjian.get_result(zongjian.name, request)\n",
    "    zongjingli.get_result(zongjingli.name, request)\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "- Manager类的get_result方法比较长，有太多的分支判断，不是好的设计；\n",
    "- Mangeer类有太多的责任，违背了单一职责的原则，增加新的管理者，需要修改这个类，违背了开放封闭原则；\n",
    "\n",
    "## 职责链模式\n",
    "职责链模式，使得多个对象都有机会处理请求，从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链，并沿着这条链传递该请求，知道有一个对象处理它为止[DP]。\n",
    "\n",
    "### 职责链模式基本结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "handler1 handle request [ 2 ]\n",
      "handler1 handle request [ 5 ]\n",
      "handler2 handle request [ 14 ]\n",
      "handler3 handle request [ 22 ]\n",
      "handler2 handle request [ 18 ]\n",
      "handler1 handle request [ 3 ]\n",
      "handler3 handle request [ 27 ]\n",
      "handler3 handle request [ 20 ]\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class Handler():\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    def __init__(self):\n",
    "        self.successor = None  # 设置继任者\n",
    "        \n",
    "    @abstractmethod\n",
    "    def handle_request(self, request):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class ConcretHandler1(Handler):\n",
    "    \"\"\"\n",
    "    处理0-10的请求\n",
    "    \"\"\"\n",
    "    def handle_request(self, request):\n",
    "        if request >=0 and request < 10:\n",
    "            print(\"handler1 handle request [ {} ]\".format(request))\n",
    "        elif self.successor != None:  # 由继任者处理请求\n",
    "            self.successor.handle_request(request)\n",
    "            \n",
    "\n",
    "class ConcretHandler2(Handler):\n",
    "    \"\"\"\n",
    "    处理10-20的请求\n",
    "    \"\"\"\n",
    "    def handle_request(self, request):\n",
    "        if request >=10 and request < 20:\n",
    "            print(\"handler2 handle request [ {} ]\".format(request))\n",
    "        elif self.successor != None:  # 由继任者处理请求\n",
    "            self.successor.handle_request(request)\n",
    "            \n",
    "\n",
    "class ConcretHandler3(Handler):\n",
    "    \"\"\"\n",
    "    处理20-30的请求\n",
    "    \"\"\"\n",
    "    def handle_request(self, request):\n",
    "        if request >=20 and request < 30:\n",
    "            print(\"handler3 handle request [ {} ]\".format(request))\n",
    "        elif self.successor != None:  # 由继任者处理请求\n",
    "            self.successor.handle_request(request)\n",
    "\n",
    "\n",
    "def main():\n",
    "    h1 = ConcretHandler1()\n",
    "    h2 = ConcretHandler2()\n",
    "    h3 = ConcretHandler3()\n",
    "    \n",
    "    h1.successor = h2\n",
    "    h2.successor = h3\n",
    "    \n",
    "    requests = [2, 5, 14, 22, 18, 3, 27, 20]\n",
    "    \n",
    "    for i in requests:\n",
    "        h1.handle_request(i)\n",
    "        \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "- 当客户提交一个请求时，请求是沿着职责链传递直至有一个ConcretHandler对象负责处理它\n",
    "- 接收者和发送者都没有对方的明确信息，且链中的对象自己也并不知道链的结构\n",
    "- 职责链可以简化对象的相互链接，它们仅需保持一个指向其后继者的引用，而不需保持它所有的候选接收者的引用\n",
    "- 可以随时增加或修改处理一个请求的结构，增强了给对象指派职责的灵活性\n",
    "- 一个请求极有可能到了链的末端都得不到处理，或者因为没有正确配置而得不到处理\n",
    "- 最重要的两点\n",
    "    - 需要实现给每个具体管理者设置它的上司，也就是它的后继者\n",
    "    - 需要在每个具体管理者类处理请求时，做出判断，是可以处理请求，还是必须“推卸责任”，转移到后继者\n",
    "    \n",
    "## 职责链模式——加薪代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "总经理:贺贺请求加薪 数量 1000 再说吧\n",
      "总监:贺贺请求请假 数量 3 被批准\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class Manager():\n",
    "    \n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.successor = None\n",
    "        \n",
    "    @abstractmethod\n",
    "    def handle_request(self, request):\n",
    "        pass\n",
    "    \n",
    "\n",
    "class CommonManager(Manager):\n",
    "    \n",
    "    def handle_request(self, request):\n",
    "        if request.type == \"请假\" and request.number <=2:\n",
    "            print(\"{}:{} 数量 {} 被批准\".format(self.name, request.content, request.number))\n",
    "        elif self.successor != None:\n",
    "            self.successor.handle_request(request)\n",
    "            \n",
    "            \n",
    "class Majordomo(Manager):\n",
    "    def handle_request(self, request):\n",
    "        if request.type == \"请假\" and request.number <=5:\n",
    "            print(\"{}:{} 数量 {} 被批准\".format(self.name, request.content, request.number))\n",
    "        elif self.successor != None:\n",
    "            self.successor.handle_request(request)\n",
    "\n",
    "        \n",
    "class GeneralManager(Manager):\n",
    "    def handle_request(self, request):\n",
    "        if request.type == \"请假\":\n",
    "            print(\"{}:{} 数量 {} 被批准\".format(self.name, request.content, request.number))\n",
    "        elif request.type == \"加薪\" and request.number <= 500:\n",
    "            print(\"{}:{} 数量 {} 被批准\".format(self.name, request.content, request.number))\n",
    "        elif request.type == \"加薪\" and request.number > 500:\n",
    "            print(\"{}:{} 数量 {} 再说吧\".format(self.name, request.content, request.number))\n",
    "            \n",
    "            \n",
    "def main():\n",
    "    jingli = CommonManager(\"经理\")\n",
    "    zongjian = Majordomo(\"总监\")\n",
    "    zongjingli = GeneralManager(\"总经理\")\n",
    "    \n",
    "    jingli.successor = zongjian\n",
    "    zongjian.successor = zongjingli\n",
    "    \n",
    "    request = Request()\n",
    "    request.type = \"加薪\"\n",
    "    request.content = \"贺贺请求加薪\"\n",
    "    request.number = 1000\n",
    "    \n",
    "    jingli.handle_request(request)\n",
    "    \n",
    "    request.type = \"请假\"\n",
    "    request.content = \"贺贺请求请假\"\n",
    "    request.number = 3\n",
    "    \n",
    "    jingli.handle_request(request)\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 职责链模式和状态模式\n",
    "- 职责链模式，主要处理requet和handler的问题，当handler收到requet时，判断是否自己是否可以处理，处理逻辑在handler中；\n",
    "- 状态模式，主要处理state和context的问题，当state变化时，改变context的state，继续调用context的处理逻辑，处理逻辑在context中；"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
